{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow.compat.v1 as tf\n",
    "tf.disable_v2_behavior()\n",
    "\n",
    "tf.reset_default_graph()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input shape: (5, 5, 1)\n",
      "\n",
      "Input Data:\n",
      " [[[0]\n",
      "  [0]\n",
      "  [1]\n",
      "  [2]\n",
      "  [3]]\n",
      "\n",
      " [[2]\n",
      "  [3]\n",
      "  [4]\n",
      "  [5]\n",
      "  [6]]\n",
      "\n",
      " [[3]\n",
      "  [4]\n",
      "  [5]\n",
      "  [6]\n",
      "  [7]]\n",
      "\n",
      " [[4]\n",
      "  [5]\n",
      "  [6]\n",
      "  [7]\n",
      "  [8]]\n",
      "\n",
      " [[5]\n",
      "  [6]\n",
      "  [7]\n",
      "  [8]\n",
      "  [9]]]\n",
      "\n",
      "Output shape: (5, 11)\n",
      "\n",
      "Output Data:\n",
      " [[0 0 0 0 1 0 0 0 0 0 0]\n",
      " [0 0 0 0 0 0 0 1 0 0 0]\n",
      " [0 0 0 0 0 0 0 0 1 0 0]\n",
      " [0 0 0 0 0 0 0 0 0 1 0]\n",
      " [0 0 0 0 0 0 0 0 0 0 1]]\n"
     ]
    }
   ],
   "source": [
    "input_train = [[[0],[0],[1],[2],[3]],[[2],[3],[4],[5],[6]],[[3],[4],[5],[6],[7]],[[4],[5],[6],[7],[8]],[[5],[6],[7],[8],[9]]]\n",
    "output_train_val = [4,7,8,9,10]\n",
    "\n",
    "output_train = []\n",
    "for t in range(0,5):\n",
    "    output_train_k = [0] * 11\n",
    "    output_train_k[output_train_val[t]] = 1\n",
    "    output_train.append(output_train_k)\n",
    "\n",
    "input_np = np.array(input_train)\n",
    "output_np = np.array(output_train)\n",
    "\n",
    "input_train = input_np.reshape(input_np.shape[0],5,input_np.shape[2])\n",
    "output_train = output_np.reshape(output_np.shape[0],11,)\n",
    "\n",
    "print(\"Input shape:\",str(input_train.shape))\n",
    "\n",
    "print(\"\\nInput Data:\\n\",str(input_train))\n",
    "\n",
    "print(\"\\nOutput shape:\",str(output_train.shape))\n",
    "\n",
    "print(\"\\nOutput Data:\\n\",str(output_train))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Training Parameters\n",
    "learning_rate = 0.001\n",
    "training_steps = 1000\n",
    "batch_size = 5\n",
    "display_step = 200\n",
    "\n",
    "# Network Parameters\n",
    "num_input = 1 \n",
    "timesteps = 5 # timesteps\n",
    "num_hidden = 128 # hidden layer num of features\n",
    "num_classes = 11 #  total classes (0-10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tf Graph input\n",
    "X = tf.placeholder(\"float\", [None, timesteps, num_input],name=\"input\")\n",
    "Y = tf.placeholder(\"float\", [None, num_classes])\n",
    "\n",
    "# Define weights\n",
    "weights = {\n",
    "    'out': tf.Variable(tf.random_normal([num_hidden, num_classes]))\n",
    "}\n",
    "biases = {\n",
    "    'out': tf.Variable(tf.random_normal([num_classes]))\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "def RNN(x, weights, biases):\n",
    "\n",
    "    # Prepare data shape to match `rnn` function requirements\n",
    "    # Current data input shape: (batch_size, timesteps, n_input)\n",
    "    # Required shape: 'timesteps' tensors list of shape (batch_size, n_input)\n",
    "\n",
    "    # Unstack to get a list of 'timesteps' tensors of shape (batch_size, n_input)\n",
    "    x = tf.unstack(x, timesteps, 1)\n",
    "\n",
    "    # Define a lstm cell with tensorflow [supported by tfllte]\n",
    "    lstm_cell = tf.compat.v1.nn.rnn_cell.BasicLSTMCell(num_hidden, forget_bias=1.0) \n",
    "\n",
    "    # Get lstm cell output [supported by tfllte]\n",
    "    outputs, states = tf.compat.v1.nn.static_rnn(lstm_cell, x, dtype=tf.float32)\n",
    "\n",
    "    # Linear activation, using rnn inner loop last output\n",
    "    return tf.matmul(outputs[-1], weights['out']) + biases['out']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "logits = RNN(X, weights, biases)\n",
    "prediction = tf.nn.softmax(logits, name=\"model_output\")\n",
    "\n",
    "# Define loss and optimizer\n",
    "loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(\n",
    "    logits=logits, labels=Y))\n",
    "optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)\n",
    "train_op = optimizer.minimize(loss_op)\n",
    "\n",
    "# Evaluate model (with test logits, for dropout to be disabled)\n",
    "correct_pred = tf.equal(tf.argmax(prediction, 1), tf.argmax(Y, 1))\n",
    "accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))\n",
    "\n",
    "# Initialize the variables (i.e. assign their default value)\n",
    "init = tf.global_variables_initializer()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "saver = tf.train.Saver()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Step 1, Minibatch Loss= 5.1239, Training Accuracy= 0.200\n",
      "Step 200, Minibatch Loss= 1.1123, Training Accuracy= 0.800\n",
      "Step 400, Minibatch Loss= 0.8893, Training Accuracy= 0.800\n",
      "Step 600, Minibatch Loss= 0.7732, Training Accuracy= 1.000\n",
      "Step 800, Minibatch Loss= 0.6951, Training Accuracy= 1.000\n",
      "Step 1000, Minibatch Loss= 0.6360, Training Accuracy= 1.000\n",
      "Optimization Finished!\n",
      "model saved\n",
      "graph saved\n"
     ]
    }
   ],
   "source": [
    "# Start training\n",
    "with tf.Session() as sess:\n",
    "\n",
    "    # Run the initializer\n",
    "    sess.run(init)\n",
    "    \n",
    "    num_of_batches =  int(len(input_train)/batch_size)\n",
    "    \n",
    "\n",
    "    for step in range(1, training_steps+1):\n",
    "        \n",
    "        ptr = 0\n",
    "        \n",
    "        for j in range(num_of_batches):\n",
    "            \n",
    "            batch_x, batch_y = input_train[ptr:ptr+batch_size], output_train[ptr:ptr+batch_size]\n",
    "\n",
    "            sess.run(train_op, feed_dict={X: batch_x, Y: batch_y})\n",
    "            \n",
    "            ptr = ptr + batch_size\n",
    "            \n",
    "            if (step % display_step == 0) or step == 1:\n",
    "                # Calculate batch loss and accuracy\n",
    "                loss, acc = sess.run([loss_op, accuracy], feed_dict={X: input_train,\n",
    "                                                                     Y: output_train})\n",
    "                print(\"Step \" + str(step) + \", Minibatch Loss= \" + \\\n",
    "                      \"{:.4f}\".format(loss) + \", Training Accuracy= \" + \\\n",
    "                      \"{:.3f}\".format(acc))\n",
    "\n",
    "    print(\"Optimization Finished!\")\n",
    "    \n",
    "    saver.save(sess, \"lstm_model_v1/lstm-model.ckpt\")\n",
    "    \n",
    "    print(\"model saved\")\n",
    "    \n",
    "    tf.train.write_graph(sess.graph.as_graph_def(), '.', 'lstm_model_v1.pbtxt', as_text=True)\n",
    "    \n",
    "    print(\"graph saved\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Restoring parameters from lstm_model_v1/lstm-model.ckpt\n",
      "INFO:tensorflow:Froze 4 variables.\n",
      "INFO:tensorflow:Converted 4 variables to const ops.\n"
     ]
    }
   ],
   "source": [
    "### freezing graph\n",
    "\n",
    "from tensorflow.python.tools import freeze_graph\n",
    "\n",
    "x = freeze_graph.freeze_graph('lstm_model_v1.pbtxt', \"\", False, \n",
    "                          'lstm_model_v1/lstm-model.ckpt', \"model_output\",\n",
    "                           \"save/restore_all\", \"save/Const:0\",\n",
    "                           'frozen_lstm_model_v1.pb', True, \"\"  \n",
    "                         )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "284856"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### covert to tflite\n",
    "\n",
    "graph_def_file = \"frozen_lstm_model_v1.pb\"\n",
    "input_arrays = [\"input\"]\n",
    "output_arrays = [\"model_output\"]\n",
    "\n",
    "converter = tf.lite.TFLiteConverter.from_frozen_graph(\n",
    "        graph_def_file, input_arrays, output_arrays)\n",
    "tflite_model = converter.convert()\n",
    "open(\"converted_lstm_model_v1.tflite\", \"wb\").write(tflite_model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input tensor:\n",
      "[[[1.]\n",
      "  [2.]\n",
      "  [3.]\n",
      "  [4.]\n",
      "  [5.]]]\n",
      "output tensor:\n",
      "[[1.01298548e-03 4.11978195e-04 5.37649903e-04 1.42181350e-03\n",
      "  1.97638854e-01 5.55840496e-04 1.12530324e-04 5.94648004e-01\n",
      "  1.70900747e-01 2.95993574e-02 3.16017610e-03]]\n",
      "\n",
      "prediction : 7\n"
     ]
    }
   ],
   "source": [
    "### doing inference of tflite model\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "# Load TFLite model and allocate tensors.\n",
    "interpreter = tf.lite.Interpreter(model_path=\"converted_lstm_model_v1.tflite\")\n",
    "interpreter.allocate_tensors()\n",
    "\n",
    "# Get input and output tensors.\n",
    "input_details = interpreter.get_input_details()\n",
    "output_details = interpreter.get_output_details()\n",
    "\n",
    "\n",
    "input_data = np.array([[[1],[2],[3],[4],[5]]], dtype=np.float32)\n",
    "\n",
    "print(\"input tensor:\")\n",
    "print(input_data)\n",
    "\n",
    "interpreter.set_tensor(input_details[0]['index'], input_data)\n",
    "\n",
    "interpreter.invoke()\n",
    "\n",
    "# The function `get_tensor()` returns a copy of the tensor data.\n",
    "# Use `tensor()` in order to get a pointer to the tensor.\n",
    "\n",
    "output_data = interpreter.get_tensor(output_details[0]['index'])\n",
    "print(\"output tensor:\")\n",
    "print(output_data)\n",
    "\n",
    "print(\"\\nprediction : \"+str(np.argmax(output_data)) )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Optimize for inference (removes params used in training not needed for inference)\n",
    "\n",
    "from tensorflow.python.tools import optimize_for_inference_lib\n",
    "\n",
    "input_graph_def = tf.GraphDef()\n",
    "with tf.gfile.Open(\"frozen_lstm_model_v1.pb\", \"rb\") as f:\n",
    "    data = f.read()\n",
    "    input_graph_def.ParseFromString(data)\n",
    "\n",
    "output_graph_def = optimize_for_inference_lib.optimize_for_inference(\n",
    "        input_graph_def,\n",
    "        [\"input\"], # an array of the input node(s)\n",
    "        [\"model_output\"], # an array of output nodes\n",
    "        tf.float32.as_datatype_enum)\n",
    "\n",
    "\n",
    "# Save the optimized graph\n",
    "\n",
    "f = tf.gfile.FastGFile(\"optimal_lstm_model_v1.pb\", \"w\")\n",
    "f.write(output_graph_def.SerializeToString())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
